package x.trident.core.sequence.starter.impl;

import com.google.common.collect.ImmutableList;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.script.RedisScript;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import x.trident.core.cache.redis.operator.DefaultRedisCacheOperator;
import x.trident.core.sequence.api.SequenceApi;
import x.trident.core.sequence.api.client.SequenceException;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 实现序列号api
 *
 * @author 林选伟
 * @date 2023-06-01
 */
@Setter
@Slf4j
public class SequenceClient implements InitializingBean, SequenceApi {

    @Resource
    private DefaultRedisCacheOperator defaultRedisCacheOperator;
    private RedisScript<Long> normalSeqNumScript;

    @Override
    public long generate(String key, boolean resetTag) {
        return executeRedisScript(normalSeqNumScript, key, resetTag);
    }

    /**
     * @param seqNumScript 脚本, 从1开始，隔日重置
     * @param key          键
     * @param <T>
     * @return
     */
    private <T> T executeRedisScript(RedisScript<T> seqNumScript, String key, boolean resetTag) {
        String today = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        List<String> keys = ImmutableList.of(key, today, resetTag ? "1" : "0");
        T seq;
        try {
            seq = defaultRedisCacheOperator.getRedisTemplate().execute((RedisCallback<T>) redisConnection -> {
                Object nativeConnection = redisConnection.getNativeConnection();
                if (nativeConnection instanceof Jedis) {
                    return (T) ((Jedis) nativeConnection).eval(seqNumScript.getScriptAsString(), keys, new ArrayList<>());
                } else if (nativeConnection instanceof JedisCluster) {
                    return (T) ((JedisCluster) nativeConnection).eval(seqNumScript.getScriptAsString(), keys, new ArrayList<>());
                } else {
                    throw new SequenceException("ERROR_SEQ1", "序列号redis异常, 不支持的模式");
                }
            });
        } catch (Exception e) {
            throw new SequenceException("ERROR_SEQ2", String.format("序列号redis异常,key:%s", key));
        }
        return seq;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 启动时实现你的加载逻辑
    }
}
